feat(codex): author marketplace policy and record distribution/install policy parity (#188 S3) - #392
Conversation
🦋 Changeset detectedLatest commit: e629711 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
…l policy parity (#188 S3) Author codex.marketplace displayName, category, and documented policy values (category follows the plugin interface category), admit every documented marketplace source form in the pinned schema, and publish dated four-state distribution rows for marketplace discovery, sources, cache layout, enable state, CLI JSON contracts, feature flags, managed requirements.toml, allow_managed_hooks_only, restrict_to_allowed_sources, and workspace publishing, backed by live codex-cli 0.147.0 probes.
5b56965 to
d7e0550
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d7e0550601
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| `Codex marketplace policy.installation must be one of ${marketplaceInstallationPolicies.join(', ')}.`, | ||
| )); | ||
| } else { | ||
| policy.installation = installation; |
There was a problem hiding this comment.
Account for NOT_AVAILABLE in the installation flow
When an author selects the newly accepted NOT_AVAILABLE policy, the marketplace correctly marks this plugin as unavailable for installation, but both the generated instructions (src/install/surface.ts:44-45) and installPublicCli (src/install/install.ts:222-230) still unconditionally run codex plugin add. Thus a valid configuration produces a bundle whose advertised and programmatic installation path is rejected by the host; either reject this policy for self-installing bundles or make those installation surfaces policy-aware.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed with a live probe: codex-cli 0.147.0 accepts codex plugin marketplace add for a NOT_AVAILABLE entry and then refuses codex plugin add demo@demo-market with plugin demo is not available for install in marketplace demo-market — the exact command INSTALL.md and installBundle() run. Fixed in a809d0d: the adapter now rejects an authored installation: NOT_AVAILABLE with codex.marketplace.policy.installation.not-installable (schema still documents the enum value; the capability row records the probe and the rejection), so the self-installing artifact can no longer advertise an install path the host refuses. AVAILABLE and INSTALLED_BY_DEFAULT remain admitted, with tests for both sides.
| }, | ||
| "gitUrl": { | ||
| "minLength": 1, | ||
| "pattern": "^(?:[Hh][Tt][Tt][Pp][Ss]?://|[Ss][Ss][Hh]://|[A-Za-z0-9._-]+@[A-Za-z0-9.-]+:)\\S+$", |
There was a problem hiding this comment.
Parse marketplace URLs instead of matching only prefixes
When this schema validates an external marketplace, malformed values such as https://%zz/repository.git satisfy this new Git URL pattern, and the analogous registry pattern on line 52 accepts the same invalid authority. These are not usable URLs, so the advertised Git/npm source validation succeeds only for Codex or npm to fail during installation; validate the HTTP(S)/SSH variants structurally rather than relying solely on a scheme-prefix regex.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agreed. Fixed in a809d0d: gitUrl now requires http(s):// or ssh:// with optional userinfo, a syntactically valid hostname (label grammar, no bare %), optional port, and a non-empty percent-encoded repository path, or an scp-style user@host:path; the npm registry pattern uses the same hostname/port/path grammar (still credential-, query-, and fragment-free HTTPS). https://%zz/repository.git, https://, https://github.com:port/..., ssh://%zz/..., git@%zz:..., and https://%zz/ registries are now rejected, with the admitted/rejected matrix covered in codex-distribution.test.ts and the schema/PROVENANCE pins updated.
…and validate Git/registry URL structure Live codex-cli 0.147.0 registers a NOT_AVAILABLE marketplace entry but refuses `codex plugin add`, the exact command INSTALL.md and installBundle() run, so an authored NOT_AVAILABLE now fails the build with codex.marketplace.policy.installation.not-installable instead of emitting an uninstallable bundle. The pinned marketplace schema's Git URL and npm registry patterns now require a syntactically valid hostname, optional port, and a percent-encoded path rather than a bare scheme prefix. Addresses the two Codex review threads on #392.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a809d0d274
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "$id": "https://agent-bundle.dev/schemas/codex/0.147.0/marketplace.schema.json", | ||
| "$defs": { | ||
| "containedPath": { | ||
| "pattern": "^\\./(?!(?:.*[\\/\\\\])?\\.\\.(?:[\\/\\\\]|$))[^\\\\]*$", |
There was a problem hiding this comment.
Reject traversal paths containing line breaks
When a local or git-subdir path contains a line break before a parent segment, such as ./safe\n/../outside, this pattern accepts it: the negative lookahead's .* does not cross line terminators, while the final [^\\]* does. Resolving that accepted path escapes the marketplace root, contradicting the containment guarantee; reject control characters or validate path segments without a dot-based lookahead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed — . in the lookahead does not cross line terminators while the trailing [^\\]* did, so ./x\n/../../outside (and \r\n, U+2028, U+2029 variants) slipped past containment. Fixed in 7e818b9: containedPath (local and git-subdir) now rejects control characters and Unicode line terminators ([^\\\u0000-\u001F\u007F\u2028\u2029]), so the lookahead sees the whole string; the same flaw existed in the pinned plugin.schema.json component and interface-asset path patterns, so those are fixed alongside (PROVENANCE notes and byte/sha pins updated). Rejection cases added in codex-distribution.test.ts and host-adapters.test.ts.
| }, | ||
| "gitUrl": { | ||
| "minLength": 1, | ||
| "pattern": "^(?:(?:[Hh][Tt][Tt][Pp][Ss]?|[Ss][Ss][Hh])://(?:[A-Za-z0-9._~!$&'()*+,;=-]+@)?[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*(?::[0-9]{1,5})?(?:/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+)+/?|[A-Za-z0-9._-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*:(?:~?[A-Za-z0-9._!$&'()*+,;=@-]+)(?:/[A-Za-z0-9._~!$&'()*+,;=@-]+)*/?)$", |
There was a problem hiding this comment.
When a Git URL uses a five-digit out-of-range port, such as https://github.com:65536/org/repo.git, this pattern still validates it even though standard URL parsing rejects it; the npm registry pattern on line 53 has the same issue. Although the previously reported malformed-authority case is fixed, this is fresh evidence that the replacement regex still admits unusable URLs, so the port must be range-checked or the values parsed structurally.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 7e818b9: the Git URL and npm registry port groups are now range-checked to 0–65535 (0*(?:0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]), leading zeros allowed as WHATWG parsing does). :65536 and :99999 are rejected for https://, ssh://, and registry URLs; :65535 and :0443 are admitted, with both sides in the test matrix.
…k URL ports in the pinned schemas The containment lookahead used `.*`, which does not cross line terminators while the trailing character class did, so `./x\n/../../outside` escaped the marketplace or plugin root. Component, interface-asset, and marketplace path patterns now reject control characters and Unicode line terminators. Git URL and npm registry ports are range-checked to 0-65535. Addresses the second-round Codex review threads on #392.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e818b9420
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }, | ||
| "gitUrl": { | ||
| "minLength": 1, | ||
| "pattern": "^(?:(?:[Hh][Tt][Tt][Pp][Ss]?|[Ss][Ss][Hh])://(?:[A-Za-z0-9._~!$&'()*+,;=-]+@)?[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*(?::0*(?:0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?(?:/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+)+/?|[A-Za-z0-9._-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*:(?:~?[A-Za-z0-9._!$&'()*+,;=@-]+)(?:/[A-Za-z0-9._~!$&'()*+,;=@-]+)*/?)$", |
There was a problem hiding this comment.
Reject invalid numeric IPv4 authorities
Fresh evidence after the earlier URL fix: this replacement pattern still accepts URLs such as https://999.999.999.999/team/plugin.git, even though standard WHATWG URL parsing rejects that authority as invalid; the registry pattern has the same gap. Consequently, an unusable Git or npm source passes artifact validation and is deferred to a host-side installation failure, so numeric IPv4-looking hosts should be parsed or range-checked rather than treated as arbitrary DNS labels.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 438b8ee. The host grammar in gitUrl, npmSource.registry, and the scp-style form is now: DNS labels whose last label contains a letter (so example.123 and 1.2.3 fail), or an in-range dotted-quad IPv4 ((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d) × 4, so 999.999.999.999, 256.1.1.1, and 1.2.3.4.5 fail). Rejected fixtures added in codex-distribution.test.ts for the HTTPS, ssh://, scp, and npm-registry forms; the admitted fixtures still cover 10.0.0.1 and 127.0.0.1:8443. PROVENANCE.json and the schema pin are updated.
| "type": "string" | ||
| }, | ||
| "source": { "const": "npm", "type": "string" }, | ||
| "version": { "minLength": 1, "pattern": "^[^/\\\\:@]+$", "type": "string" } |
There was a problem hiding this comment.
Validate npm version selectors rather than arbitrary text
When a marketplace entry supplies a malformed selector such as version: "foo bar" or version: "%", this pattern accepts it even though npm 11.4.2 rejects the resulting package specs with EINVALIDTAGNAME. Since this schema is intended to validate real-world npm marketplace sources before installation, restrict the field to valid versions, ranges, and dist-tags instead of merely excluding path/URL punctuation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 438b8ee. npmSource.version is now a three-alternative pattern: a semver version (x.y.z[-pre][+build], optional v/= prefix), a semver range (comparator sets with x/X/* wildcards, ~/^/>=… operators, hyphen ranges, || unions), or a dist-tag composed only of characters encodeURIComponent leaves unescaped — the same rule npm-package-arg applies before raising EINVALIDTAGNAME. foo bar, %, "", 1.2.3 || foo bar, >=1.0.0 <, latest@1, a/b, a:b, a#b, and a?b are now rejected fixtures; latest, next, 1.2.3, ^1.2.0, >=1.0.0 <2.0.0, 1.x, 1.2.3-beta.1+build.5, and 1.0.0 - 2.0.0 remain admitted.
…sion selectors in the marketplace schema Hosts must be DNS labels whose last label contains a letter or an in-range dotted-quad IPv4, so https://999.999.999.999/repo.git no longer passes; npm `version` must be a semver version, a semver range, or a dist-tag composed only of characters encodeURIComponent leaves unescaped, matching the npm-package-arg rules that otherwise fail with EINVALIDTAGNAME. Addresses the third-round Codex review threads on #392.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 438b8ee9fe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }, | ||
| "gitUrl": { | ||
| "minLength": 1, | ||
| "pattern": "^(?:(?:[Hh][Tt][Tt][Pp][Ss]?|[Ss][Ss][Hh])://(?:[A-Za-z0-9._~!$&'()*+,;=-]+@)?(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?:[:/]|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?::0*(?:0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?(?:/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+)+/?|[A-Za-z0-9._-]+@(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?:[:/]|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?):(?:~?[A-Za-z0-9._!$&'()*+,;=@-]+)(?:/[A-Za-z0-9._~!$&'()*+,;=@-]+)*/?)$", |
There was a problem hiding this comment.
Accept bracketed IPv6 hosts in marketplace URLs
When a Git or npm marketplace is hosted on an IPv6-only service, valid URLs such as ssh://git@[2001:db8::1]/team/repo.git and https://[2001:db8::1]:4873/ are rejected because this host grammar admits only DNS names and dotted-quad IPv4; the registry pattern on line 53 has the same limitation. Git accepts the SSH form as a remote URL, so these sources fail artifact validation before Codex can use them; parse the authority structurally or add bracketed IPv6 support to both patterns.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 82c0c41. The shared host grammar in gitUrl (both the scheme and scp-style branches) and npmSource.registry now has a third alternative: a bracketed RFC 3986 IP-literal covering every IPv6 form (full eight groups, :: compression at any position, and IPv4-embedded tails such as [::ffff:10.0.0.1]). Admitted fixtures: ssh://git@[2001:db8::1]/team/codex-plugins.git, https://[2001:db8::1]:4873/…, https://[::1]/…, git@[2001:db8::1]:team/codex-plugins.git, and registries https://[2001:db8::1]:4873/ / https://[::1]/. Rejected: unbracketed https://2001:db8::1/…, unterminated [2001:db8::1, non-hex groups, nine groups, seven uncompressed groups, and []. Pins and PROVENANCE notes updated.
| "type": "string" | ||
| }, | ||
| "source": { "const": "npm", "type": "string" }, | ||
| "version": { "minLength": 1, "pattern": "^(?:(?:v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)? - v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?|(?:(?:[<>]=?|=|~>?|\\^) ?)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?(?: (?:(?:[<>]=?|=|~>?|\\^) ?)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?)*)(?: ?\\|\\| ?(?:v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)? - v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?|(?:(?:[<>]=?|=|~>?|\\^) ?)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?(?: (?:(?:[<>]=?|=|~>?|\\^) ?)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?)*))*|[A-Za-z0-9!'()*._~-]+)$", "type": "string" } |
There was a problem hiding this comment.
Accept valid whitespace in npm semver ranges
Fresh evidence after the earlier selector fix: a valid npm range such as >=1.0.0 <2.0.0 is rejected because comparator intersections and hyphen ranges require exactly one ASCII space. npm 11.4.2's bundled npm-package-arg classifies this selector as a range and semver.validRange accepts it, so real-world marketplaces containing repeated whitespace or tabs now fail artifact validation before Codex/npm can consume them; use npm's whitespace grammar rather than literal single spaces.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 82c0c41. The version pattern now follows semver's Range normalisation (range.trim().split(/\s+/) plus the COMPARATORTRIM/HYPHENRANGE \s+ rules): any whitespace run between comparators, between an operator and its version, and around the hyphen in a hyphen range, with leading/trailing whitespace tolerated. Newly admitted fixtures: >=1.0.0 <2.0.0, >=1.0.0\t<2.0.0, >= 1.0.0, ~ 1.2, 1.2.3, 1.0.0 - 2.0.0, ^1||^2. 1.0.0 -2.0.0 is added as a rejected fixture since semver parses -2.0.0 as an invalid comparator there.
…n marketplace URL and version patterns Git URLs, scp-style remotes, and npm registries now admit RFC 3986 IP-literal hosts such as ssh://git@[2001:db8::1]/repo.git and https://[::1]:4873/, and npm `version` follows semver's whitespace grammar (any whitespace run between comparators or around a hyphen range, trimmed at the ends) instead of a single literal space. Addresses the fourth-round Codex review threads on #392.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 82c0c413bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "type": "string" | ||
| }, | ||
| "source": { "const": "npm", "type": "string" }, | ||
| "version": { "minLength": 1, "pattern": "^(?:\\s*(?:v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?\\s+-\\s+v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?|(?:(?:[<>]=?|=|~>?|\\^)\\s*)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?(?:\\s+(?:(?:[<>]=?|=|~>?|\\^)\\s*)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?)*)(?:\\s*\\|\\|\\s*(?:v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?\\s+-\\s+v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?|(?:(?:[<>]=?|=|~>?|\\^)\\s*)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?(?:\\s+(?:(?:[<>]=?|=|~>?|\\^)\\s*)?v?(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:\\.(?:[xX*]|0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)?)?)*))*\\s*|[A-Za-z0-9!'()*._~-]+)$", "type": "string" } |
There was a problem hiding this comment.
Reject path-like npm version selectors
Fresh evidence after the earlier selector fix: when an npm source uses version: ".", "..", or ".foo", the final dist-tag alternative accepts it, but npm 11.4.2's bundled npm-package-arg classifies each as type: "directory" (isPosixFile treats any dot-prefixed spec as a file spec), not as a registry version, range, or tag. These accepted values therefore violate the documented prohibition on path selectors and can make Codex resolve a local directory or fail installation instead of fetching the requested package; exclude dot-prefixed path forms or validate the selector with npm's parser.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in c395a7e. The dist-tag alternative now carries a (?!\.) guard so ., .., .foo, and .latest (all type: "directory" under npm-package-arg's isFileSpec) are rejected; those four are added as rejected fixtures and the PROVENANCE note records the rule. The other file-spec prefixes (/, \\, ~/, drive letters) were already excluded by the character class.
| "pattern": "^(?:(?:[Hh][Tt][Tt][Pp][Ss]?|[Ss][Ss][Hh])://(?:[A-Za-z0-9._~!$&'()*+,;=-]+@)?(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|\\[(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)\\]|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?:[:/]|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?::0*(?:0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?(?:/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+)+/?|[A-Za-z0-9._-]+@(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|\\[(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)\\]|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?::|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?):(?:~?[A-Za-z0-9._!$&'()*+,;=@-]+)(?:/[A-Za-z0-9._~!$&'()*+,;=@-]+)*/?)$", | ||
| "type": "string" | ||
| }, | ||
| "gitRef": { "minLength": 1, "type": "string" }, |
There was a problem hiding this comment.
Validate Git refs before admitting marketplace sources
When a URL or git-subdir source supplies an invalid ref such as ref: "foo bar", this definition accepts it solely because it is nonempty, even though git check-ref-format --branch 'foo bar' exits 128 with fatal: 'foo bar' is not a valid branch name; control characters, .., @{, and .lock suffixes are admitted similarly. Such a marketplace passes artifact validation only to become unresolvable during Git installation, so apply Git's ref-name restrictions rather than accepting arbitrary text.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in c395a7e. gitRef now has a pattern implementing git check-ref-format: no control characters, space, or ~^:?*[\\; no .. or @{; components cannot be empty, start with ., or end with . or .lock; no leading - (so a ref cannot be parsed as a git option) and no lone @. Admitted fixtures: main, release/1.2, v1.2.3, refs/tags/v1.2.3, feature/foo.bar, HEAD, user/-dash, x.lockfile, a@b. 24 rejected refs (foo bar, foo..bar, foo@{1}, @, -main, main.lock, a.lock/b, refs/heads/, /main, a//b, main., .hidden, a/.b, each forbidden character, tab, DEL, newline) are asserted for both url and git-subdir sources.
… dot-prefixed npm dist-tags
`ref` selectors now follow git check-ref-format (no control characters,
whitespace, ~^:?*[\, "..", or "@{"; no empty, dot-leading, ".lock"- or
dot-trailing components; no leading "-" or lone "@"), and npm `version`
dist-tags may not start with "." because npm-package-arg classifies those as
directory specs rather than registry selectors.
Addresses the fifth-round Codex review threads on #392.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c395a7e4ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }, | ||
| "gitUrl": { | ||
| "minLength": 1, | ||
| "pattern": "^(?:(?:[Hh][Tt][Tt][Pp][Ss]?|[Ss][Ss][Hh])://(?:[A-Za-z0-9._~!$&'()*+,;=-]+@)?(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|\\[(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)\\]|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?:[:/]|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?::0*(?:0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?(?:/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+)+/?|[A-Za-z0-9._-]+@(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|\\[(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)\\]|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)*(?=[A-Za-z0-9-]{1,63}(?::|$))(?=[0-9-]*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?):(?:~?[A-Za-z0-9._!$&'()*+,;=@-]+)(?:/[A-Za-z0-9._~!$&'()*+,;=@-]+)*/?)$", |
There was a problem hiding this comment.
Accept standard Git URL userinfo
Fresh evidence after the earlier authority fix: when a private marketplace uses a standard credentialed URL such as https://user:token@git.example.test/team/plugin.git, this pattern rejects it because the optional userinfo character class omits : and percent escapes. Git 2.43 parses that form and proceeds to connect, while the schema already intends to support userinfo by accepting https://token@...; parse the URL structurally or expand the userinfo grammar so usable authenticated Git sources are not rejected before Codex can resolve them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in e629711. The gitUrl userinfo group is now RFC 3986 *( unreserved / pct-encoded / sub-delims / ":" ), so https://user:token@git.example.test/…, https://user%40corp:p%3Ass@…, and credentialed IPv4/IPv6 authorities are admitted (fixtures added). Rejected fixtures confirm credentials cannot mask a missing or malformed authority (user:token@/…, user:token@%zz/…, :65536, 999.999.999.999), and that raw spaces, bad escapes (us%zzer@), and a doubled @ still fail. npmSource.registry deliberately stays credential-free as documented in PROVENANCE, since npm reads registry auth from .npmrc, not the URL.
… in marketplace Git URLs Credentialed Git sources such as https://user:token@git.example.test/repo.git are valid to Git and were already half-supported (bare token@ passed); the userinfo grammar now matches RFC 3986 while still refusing a masked bad authority, raw spaces, malformed escapes, or a repeated "@". Addresses the sixth-round Codex review thread on #392.
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Slice 3 of #188 (marketplace / install / policy). Stacked on #378 (merged).
codex.marketplaceauthors the emitted.agents/plugins/marketplace.json: pickerdisplayName, entrycategory(defaults to the plugin's interface category instead of a hard-codedProductivity), andpolicy.installation/policy.authenticationvalidated against the documented values (codex.marketplace.*diagnostics). The source stays the local./plugin root.marketplace.schema.jsonnow admits every documented source form — plain./string,{ source: local }, Git rooturl(+ref/sha),git-subdir, andnpm(package,versionwithout path/URL selectors, credential-free HTTPSregistry) — with root containment and backslash rejection; tightenings recorded in PROVENANCE.distributioncapability block (18 dated four-state rows) mirrored as adapter capabilities and intersected to honestunavailableon the unified target: repo/personal/legacy-Claude marketplace discovery, marketplace sources/interface/policy/category, install cache layout,config.tomlenable state,codex pluginandcodex plugin marketplaceJSON contracts,features.plugins/features.hooks(+ deprecatedcodex_hooks), inline[hooks]TOML, managedrequirements.toml,allow_managed_hooks_only,restrict_to_allowed_sources, workspace publishing (features.plugin_sharing). Submission/publish controls remain recorded as distribution policy (manifestPackage.submissionPolicy), not compiler behavior..../$PLUGIN/local/; codex-cli 0.147.0 uses the manifest version segment (installedPath=.../review-tools/1.2.3).Evidence
AGENT_BUNDLE_NATIVE_HOST_CONTRACTS=1, isolatedCODEX_HOME, codex-cli 0.147.0): new testpins the Codex plugin and marketplace CLI JSON contracts, cache layout, and enable state for an authored marketplace policy—marketplace add/list/upgrade/remove --json,plugin add/list/remove --jsonfields match the documented contracts; authoredINSTALLED_BY_DEFAULT/ON_USEechoed asinstallPolicy/authPolicy;config.toml[plugins."<id>"] enabled = true+[marketplaces.<name>]written and removed. 3/3 Codex native probes passed locally.tests/codex-distribution.test.ts(13 tests: rows, source-form matrix, defaults, authored fields, diagnostics).pnpm typecheck✅,pnpm lint✅ (0 errors),pnpm test:route-unit✅ 35,pnpm test:projection✅ 63,pnpm build && pnpm test:integration:run✅ 938/938 (pre-rebase run).pnpm test:unit: 2689 passed; 4 failures were load-induced timeouts on a machine at load average 150 (event-ipclive-server collision,mcp-probe-serviceteardown timeout,native-claude-contract5 s timeouts,rsc-runtime/dispatcher150 ms deadline) in modules this PR does not touch;native-claude-contractandlifecycle-replaypass when re-run with a longer timeout.Test plan
pnpm exec rstest --config rstest.unit.config.ts packages/agent-bundle/tests/codex-distribution.test.ts packages/agent-bundle/tests/codex-hook-contract.test.ts packages/agent-bundle/tests/adapter-metadata.test.ts packages/agent-bundle/tests/adapter-capability-states.test.ts packages/agent-bundle/tests/host-adapters.test.ts